
#include "YModemMaster.h" 

/*** Interior Define */
#define LLOG_ERR
// #define LLOG_MARK
#define LLOG
#include "llog.h"

#ifdef	CFG_SYS_MALLOC

#define L_MALLOC 	MALLOC
#define L_FREE 		MFREE

#else

#include <stdlib.h>
#define L_MALLOC(A) malloc(A)
#define L_FREE(A)	free(A)

#endif


#define YM_M_START	0x00
#define YM_M_IDLE	0xFE
#define YM_M_EXIT	0xFF
/*** Interior Struct */
struct CC_YModem_Master_StateMachine{

	uint8_t 	stage;
	uint8_t  	Sn; 
	uint16_t 	DLen;		// cfg // RYM_SOH_DATA_SZ or RYM_STX_DATA_SZ 传输数据块时使用的长度
	uint32_t 	WaitTick;	// cfg 
	uint32_t 	MarkTick;
	uint8_t*	pBuf;		// SendBuf
	uint16_t 	BufSz;	
	
	//
	uint32_t 	SCnt;	
	uint32_t 	FileSz;
	uint8_t*	pFileName;	
	
	int (*pSend)(uint16_t Len,uint8_t* pPkg);	//数据发送API
	uint32_t (*pFileRead)(uint32_t Offset,uint32_t Len,uint8_t* pRBuf);	
	int(*pExit)(tYM_Master* pObj);
};
/*** Global Data */
static uint32_t g_SumSnCnt = 0;
static uint8_t g_FileSz_Mode = 0; // 0=Hex,1=Dec
/*** Local Function Define*/

//========================//

#ifdef CFG_YMODEM_MASTER	

void YM_Master_Show(tYM_Master* pObj){
	
	LLOG("stage		=[%x]\n",pObj->stage	);
	LLOG("Sn 		=[%x]\n",pObj->Sn		);
	LLOG("DLen		=[%x]\n",pObj->DLen	);
	LLOG("MarkTick	=[%x]\n",pObj->MarkTick);
	LLOG("pBuf		=[%x]\n",pObj->pBuf	);
	LLOG("BufSz		=[%x]\n",pObj->BufSz	);
	LLOG("SCnt		=[%x]\n",pObj->SCnt	);
	LLOG("FileSz	=[%x]\n",pObj->FileSz	);
	LLOG("pFileName	=[%x]\n",pObj->pFileName);
	LLOG("pSend		=[%x]\n",pObj->pSend	);
	LLOG("pFileRead	=[%x]\n",pObj->pFileRead);

}

tYM_Master* YM_Master_SCreate(uint32_t BufSz){
	
	tYM_Master* ptmp=NULL;
	uint8_t* pBuf;
	uint32_t AllocSz;
	uint32_t ObjSz;
	
	ObjSz=sizeof(tYM_Master);
	AllocSz=(ObjSz&0x03)?4:0;
	AllocSz+=ObjSz&(~0x03);
	
	pBuf = L_MALLOC(AllocSz+BufSz);
	if(pBuf == NULL){ LLOG_ERR(); return ptmp;}
	
	MEMSET(pBuf,0,AllocSz+BufSz);
	ptmp=(tYM_Master*)&pBuf[0];	
	//
	ptmp->BufSz	= BufSz;
	ptmp->pBuf 	= &pBuf[AllocSz];
	ptmp->stage	= YM_M_IDLE;	
	return ptmp;
}

int YM_Master_BindCallback(tYM_Master* pObj,const tYMM_CallBackAPI* pAPI){
	//
	int ret = RET_ERR;
	if(pObj == NULL) {  LLOG_ERR(); return ret; }
	if(pAPI == NULL) {  LLOG_ERR(); return ret; }
	ret = RET_OK;
	
	pObj->pFileRead = pAPI->pFileRead;
	pObj->pExit		= pAPI->pExit;
	
	// pObj->Flag	|= 0x01;
	
	return ret;
}

int YM_Master_BindSend(tYM_Master* pObj,int (*pFunc)(uint16_t,uint8_t*) ){
	
	int ret=RET_ERR;
	if (pObj == NULL) { LLOG_ERR();  return ret;}
	
	pObj->pSend = pFunc;
	ret=RET_OK;
	
	return ret;
}

// Para set
int YM_Master_StartTransFile(tYM_Master* pObj,uint32_t FileSz,uint8_t* pStrFileName){
	
	int ret=RET_ERR;
	if (pObj == NULL) { LLOG_ERR();  return ret;}
	
	ret=RET_OK;
	
	pObj->FileSz	= FileSz;
	pObj->pFileName	= pStrFileName;
	pObj->stage		= YM_M_START;		// Start Server

	return ret;	
}

int YM_Master_SetPara(tYM_Master* pObj,tMs WaitTick,uint16_t DPkgSz){
	int ret=RET_ERR;
	if (pObj == NULL) 				{ LLOG_ERR();  return ret;}
	if (pObj->stage	!= YM_M_IDLE) 	{ LLOG_ERR();  return ret;}
	
	ret=RET_OK;
	pObj->WaitTick 	= WaitTick;
	pObj->DLen 		= DPkgSz; 
}

int YM_Master_SetFileSzMode(uint8_t HexOrDec){
	// 0=Hex,else=Dec 
	if(0 == HexOrDec){
		g_FileSz_Mode=0;
	}else{
		g_FileSz_Mode=1;
	}
	return RET_OK;
}

#endif

#ifdef CFG_YMODEM_MASTER

static uint16_t YMM_LoadFileData(tYM_Master *pObj,uint16_t LoadNum,uint8_t* pRbuf){

	uint32_t ResNum;
	uint32_t RNum = 0;
	int i;
	
	if(pObj == NULL) 				{ LLOG_ERR(); return RNum;}
	if(pObj->pFileRead == NULL) 	{ LLOG_ERR(); return RNum;}

	if( (pObj->SCnt) >= (pObj->FileSz) ){ return RNum; }
	
	RNum = pObj->pFileRead(pObj->SCnt,LoadNum,pRbuf);
	pObj->SCnt += RNum;
	ResNum = LoadNum - RNum;
	if(ResNum > 0){
		LLOG("#I Fill LoadNum[%d],RNum[%d] ResNum[%d]\n",LoadNum,RNum,ResNum);
		// LLOG("FileSz[%d]-SCnt[%d]\n",pObj->SCnt,pObj->FileSz);
		for(i=0;i<ResNum;i++){	
			pRbuf[RNum+i] = 0x00;
		}		
	}
	return RNum;
}

static int Send_NextDataPkg(tYM_Master* pObj,uint8_t Sn,uint8_t CtrlFlag){
	// next 
	int ret = RET_ERR;
	int i;
	uint8_t* pTmpBuf;
	uint16_t crc16;
	uint16_t Len ;
	uint16_t FLen = 0;
	
	Len = pObj->DLen;
	pTmpBuf = pObj->pBuf;
	
	// Para Check
	if (pObj->BufSz < (Len + RYM_MIN_SZ)) { LLOG_ERR(); return FLen; }
	
	i = 0;
	if(Len == RYM_SOH_DATA_SZ){
		pTmpBuf[i++] = RYM_CODE_SOH;
	}
	else if(Len == RYM_STX_DATA_SZ){
		pTmpBuf[i++]=RYM_CODE_STX;
	}
	else{
		LLOG_ERR(); return FLen;
	}
	
	pTmpBuf[i++]=Sn;
	pTmpBuf[i++]=(~Sn);
	
	if( CtrlFlag == 0 ){	// send ZeroPkg
		MEMSET(&pTmpBuf[i],0x00,Len);		
	}
	else{
		FLen = YMM_LoadFileData(pObj,pObj->DLen,&pTmpBuf[i]);
		if(FLen == 0){ 
			return FLen;
		}
	}
	i+=Len;
	
	crc16=Calc_Pkg_CRC16(0,Len,&pTmpBuf[3]);
	pTmpBuf[i++]=(crc16>>8)&0xFF;
	pTmpBuf[i++]=(crc16&0xFF);
	
	//
	if(pObj->pSend != NULL){
		ret = pObj->pSend(i,&pTmpBuf[0]);
	}
	
	return FLen;
}
//
static int SendCmd(tYM_Master* pObj,uint8_t Cmd){
	int ret = RET_ERR;
	uint8_t tmp = Cmd;
	
	if(pObj->pSend != NULL){
		ret = pObj->pSend(1,&tmp);
	}	
	return ret;
}

static int Send_FristPkg(tYM_Master* pObj,uint32_t FileSz,uint8_t* pFileName){
	
	int ret = RET_ERR;
	uint16_t crc16;
	int i,j;
	uint16_t Len;
	uint16_t Cnt;
	uint8_t  FileSzStr[9];
	uint8_t* pTmpBuf;
	int StrNum;
	
	pTmpBuf = pObj->pBuf;
	Len = RYM_SOH_DATA_SZ;
	if(pObj->BufSz < (Len+RYM_MIN_SZ))	{ LLOG_ERR(); return ret; }
	
	i=0;
	pTmpBuf[i++] = RYM_CODE_SOH;

	pTmpBuf[i++] = 0;
	pTmpBuf[i++] = (~0);
	// 
	Cnt=0;
	j=0;
	while(pFileName[j] != 0x00){		// String Copy
		pTmpBuf[i++]=pFileName[j++];
	}
	Cnt+=j;
	pTmpBuf[i++]=0x00;
	Cnt++;
	
	if(g_FileSz_Mode == 0){
		StrNum=u32ToHexStr( FileSz,&FileSzStr[0]);
	}
	else{ 
		StrNum=u32ToDecStr( FileSz,&FileSzStr[0]);
	}	
	
	j=0;
	while(FileSzStr[j] != 0x00){
		pTmpBuf[i++]=FileSzStr[j++];
	}
	Cnt+=j;
	pTmpBuf[i++]=0x00;	
	Cnt++;
	
	MEMSET(&pTmpBuf[i],0x00,(Len-Cnt));
	i+=(Len-Cnt);
	
	crc16=Calc_Pkg_CRC16(0,Len,&pTmpBuf[3]);
	pTmpBuf[i++]=(crc16>>8)&0xFF;
	pTmpBuf[i++]=(crc16&0xFF);

	// 
	if(pObj->pSend != NULL){
		ret = pObj->pSend(i,&pTmpBuf[0]);
	}
	return ret;
}

int SM_YModem_Master(tYM_Master *pObj,uint16_t Len,uint8_t* pPkg){
	
	int ret=RET_ERR;
	volatile uint8_t State;
	uint16_t FLen;
	uint8_t RCmd;
	
	if(pObj == NULL) { LLOG_ERR(); return ret; }
	
	State=pObj->stage;
	if( State == YM_M_IDLE){ 
		// if(g_Flg_YModem_ChannelSelect!= 0){
			// YModem_Updata_OnOff(0);
		// }
		// g_Flg_YModem_ChannelSelect =0;
		// LLOG_MARK();
		return ret;
	}
	
	if (State > YM_M_START) {
		if (Check_TimeOut(pObj->MarkTick, 10000) == RET_OT) {
			pObj->stage = YM_M_EXIT;LLOG_ERR();	
		}
	}
	
	if((pPkg != NULL) && (Len > 0)){
		RCmd=pPkg[0];
	}
	
	switch(State){
		case 0:{ // Start Init
			if( (pObj->pFileRead == NULL)\
			||	(pObj->pSend == NULL) ){ 
				State = YM_M_EXIT; LLOG_ERR();
			}
			pObj->SCnt		= 0;
			pObj->Sn 		= 0;
			pObj->MarkTick=GetNowTick();
			State = 1;
			g_SumSnCnt = 0;
			
			LLOG("#I YModem_Master Start\n");

		}break;
		case 1:{ // Wait C-Pkg And SendFristPkg
/* 			if(Check_TimeOut(pObj->MarkTick,pObj->WaitTick) == RET_OT){
				pObj->MarkTick = GetNowTick();
				State=YM_M_EXIT;LLOG_MARK();
			}	 */
			
			if(Len == 0)	{ return ret; }
			
			pObj->MarkTick = GetNowTick();
			if (RCmd == RYM_CODE_CAN) {
				State = YM_M_EXIT; LLOG_ERR();
				break;
			}
			
			if (RCmd != RYM_CODE_C) {
				LLOG_MARK(); return ret;
			}
			// LLOG_MARK();
			Send_FristPkg(pObj,pObj->FileSz,pObj->pFileName);
			LLOG("#I YM Start TrnasFile:File[%s]Byte[0x%x]\n",pObj->pFileName,pObj->FileSz);
			LLOG("[");
			State++;
		}break;
		case 2:{ // WaitACK
			if(Len == 0)	{ return ret; }
			
			pObj->MarkTick = GetNowTick();
			if(RCmd == RYM_CODE_CAN){
				State = YM_M_EXIT; LLOG_ERR();
				break;
			}
			if(RCmd != RYM_CODE_ACK){ 
				LLOG_MARK(); return ret;
			}
			
			pObj->Sn++;
			State++;
		}break;
		
		case 3:{ // DataPkgTrans-Send

			FLen = Send_NextDataPkg(pObj,pObj->Sn,1);
			if(FLen == 0){ 
				if( (pObj->SCnt) == (pObj->FileSz) ){ 
					State++;
					SendCmd(pObj, RYM_CODE_EOT);
					LLOG("]\n");
					LLOG("#I M2S PkgSum[%d] End\n",g_SumSnCnt);
				}
				else{
					State = YM_M_EXIT; LLOG_ERR();
					LLOG("...ERR]\n");
				}
				break;
			}
			
			LLOG("=");
			g_SumSnCnt++;
			State--;
			
		}break;
		
		case 4:{ // WaitNAK+SendCmd(RYM_CODE_EOT)2
			if(Len == 0)	{ return ret; }
			
			pObj->MarkTick = GetNowTick();
			if(RCmd == RYM_CODE_CAN){
				State = YM_M_EXIT; LLOG_ERR(); 
				break;
			}
			if(RCmd != RYM_CODE_NAK){
				LLOG_MARK(); return ret;
			}
			
			SendCmd(pObj,RYM_CODE_EOT);//2
			State++;
		}break;
		case 5:{ // WaitACK
			if(Len == 0)	{ return ret; }
			
			pObj->MarkTick = GetNowTick();
			if(RCmd == RYM_CODE_CAN){ 	
				State = YM_M_EXIT; LLOG_ERR(); 
				break;
			}		
			
			if(pPkg[0] != RYM_CODE_ACK){ 
				LLOG_MARK(); return ret;
			}
			State++;
		}break;		
		case 6:{ // Wait C-Pkg + Send-NullDataPkg
			if(Len == 0)	{ return ret; }	
			
			pObj->MarkTick = GetNowTick();
			if(RCmd == RYM_CODE_CAN){
				State = YM_M_EXIT; LLOG_ERR(); 
				break;
			}			
			
			if(RCmd != RYM_CODE_C){ 
				LLOG_MARK(); return ret;
			}
			
			FLen = Send_NextDataPkg(pObj,0,0);
			State++;
		}break;
		case 7:{ // WaitACK+Finish
			if(Len == 0){
				return ret;
			}	
			pObj->MarkTick = GetNowTick();
			if(RCmd == RYM_CODE_CAN){
				State = YM_M_EXIT; LLOG_ERR(); 
				break;
			}
			
			if(RCmd != RYM_CODE_ACK){ 
				LLOG_MARK(); return ret; 
			}
			LLOG("#I YM Trans Done OK\n");
			State = YM_M_EXIT;
		}break;
		default:{
			State = YM_M_EXIT; LLOG_ERR();
		}
	}
	
	if(State == YM_M_EXIT){// Err Process
		
		if(pObj->pExit != NULL){
			pObj->pExit(pObj);
		}
		LLOG("FileSz[%d]-SCnt[%d]\n",pObj->FileSz,pObj->SCnt);
		State = YM_M_IDLE;
	}
	pObj->stage=State;
	return ret;
}

#endif

